package com.minelx.table.core.series;

import com.minelx.table.core.series.index.Indexes;
import com.minelx.table.core.series.source.Source;

import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.joining;

public class Series<E> implements Source<E> {

	private final Indexes indexes;

	private final Source<E> decorated;

	private Series(Indexes indexes, Source<E> decorated) {
		this.indexes = indexes;
		this.decorated = decorated;
	}

	public Source<E> sequential() {
		return new Source<E>() {
			@Override
			public E at(long at) {
				return decorated.at(indexes.at(at));
			}

			@Override
			public long len() {
				return indexes.len();
			}

			@Override
			public Stream<E> stream() {
				return indexes.stream(decorated);
			}
		};
	}

	@Override
	public E at(long at) {
		return decorated.at(at);
	}

	@Override
	public long len() {
		return indexes.len();
	}

	@Override
	public Stream<E> stream() {
		return indexes.stream(decorated);
	}

	public Indexes indexes() {
		return indexes;
	}

	public String toText() {
		return indexes.stream()
				.mapToObj(index -> index + "\t" + decorated.at(index))
				.collect(joining("\n"));
	}

	public Series<E> mapIndexes(UnaryOperator<Indexes> mapper) {
		return Series.create(mapper.apply(indexes), decorated);
	}

	public long[] indexesOf(E element) {
		return indexes.stream()
				.filter(index -> decorated.at(index).equals(element))
				.toArray();
	}

	public long indexOf(E element) {
		return indexes.stream()
				.filter(index -> decorated.at(index).equals(element))
				.findAny().orElse(-1);
	}

	public <R> Series<R> mapSource(Function<E, R> mapper) {
		return Series.create(indexes, decorated.map(mapper));
	}

	public static <E> Series<E> create(Indexes indexes, Source<E> source) {
		return new Series<>(indexes, source);
	}

	// FIXME 2021/8/16  THIS METHOD NEEDED TEST SUPPORT!

	public static <E> Series<E> fill(E by, Indexes indexes) {
		// FIXME 2021/8/18  wait for me!!!     length isn't that good.
		return Series.create(indexes, Source.fill(by, indexes.len()));
	}

	// FIXME 2021/8/16  THIS METHOD NEEDED TEST SUPPORT!

	public static <E> Series<E> create(Source<E> source) {
		return Series.create(Indexes.range(0, source.len()), source);
	}
}
